package cn.tedu.charging.user.service;

import cn.tedu.charging.common.constant.AppInfoConst;
import cn.tedu.charging.common.enums.VehicleUserBindEnum;
import cn.tedu.charging.common.pojo.param.ChargeParam;
import cn.tedu.charging.common.pojo.param.VehicleBindParam;
import cn.tedu.charging.common.pojo.po.ChargingUserInfoPO;
import cn.tedu.charging.common.pojo.po.ChargingUserVehicleBindPO;
import cn.tedu.charging.common.pojo.po.ChargingVehiclePO;
import cn.tedu.charging.common.pojo.vo.BalanceVO;
import cn.tedu.charging.common.pojo.vo.VehicleVO;
import cn.tedu.charging.user.mapper.UserMapper;
import cn.tedu.charging.user.repository.UserRepository;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class UserServiceImpl implements UserService{
    @Autowired
    private UserRepository userRepository;

    @Override
    public Integer wxLogin(String nickName, String code) {
        //1.使用授权码 获取授权服务器的openId
        String openId=getOpenId(code);
        log.debug("从授权服务器获取openId:{},使用的code授权码:{}",openId,code);
        //2.判断是否正确获取
        if (openId==null||openId.length()==0){
            log.error("获取openId失败");
            throw new RuntimeException("获取openId失败");
        }
        //3.查询本地车主是否有绑定这个openId的数据
        ChargingUserInfoPO po=userRepository.getUserByOpenId(openId);
        //4.判断本地车主是否有绑定这个openId的数据
        if (po==null){
            //5.组织一个新的userInfoPO 补充必要属性写入到数据库
            //将新生成po返回id;
            po=new ChargingUserInfoPO();
            //openId 绑定
            po.setWxOpenId(openId);
            //nickname
            po.setNickName(nickName);
            //create update
            po.setCreateTime(new Date());
            po.setUpdateTime(new Date());
            userRepository.saveUser(po);
            //如果是框架新增的po 在po的属性id上添加了自增注解,id会回填
        }
        return po.getId();
    }

    /**
     * 如果企业输出日志有要求,有格式,一般打印日志位置
     * 方法进入
     * 方法返回
     * 业务分支
     * @param userId
     * @return
     */
    @Override
    public BalanceVO myBalance(Integer userId) {
        log.debug("查询用户余额,userId:{}",userId);
        //1.余额数据 在数据库表 字段balance 先查询userPO
        ChargingUserInfoPO po=userRepository.getById(userId);
        //2.生成vo 根据非空数据封装
        BalanceVO vo=null;
        if (po!=null){
            log.debug("查询到用户数据:{}",po);
            vo=new BalanceVO();
            vo.setBalance(po.getBalance());
        }
        log.debug("查询用户余额,userId:{},查到结果:{}",userId,vo==null?"null":vo);
        return vo;
    }

    @Override
    public void charge(ChargeParam param) {
        //1.严谨的思路 判断用户在不在 不在抛异常
        //调用仓储层 执行更新
        userRepository.updateBalanceByUserId(param);
    }

    @Override
    public VehicleVO bindedVehicle(Integer userId) {
        //1.使用userId,以及状态state=1 查询查询用户绑定车辆
        //对于状态 状态码 字典类型 变动不大的数据 可以放到枚举和常量
        //星期 月 地市 非常适合做枚举 但是数据量一旦大了,就应该放到字典表
        List<ChargingUserVehicleBindPO> pos=
                userRepository.getBindedVehicle(userId, VehicleUserBindEnum.BINDED.getState());
        //2.判断是否有数据 判断数据是否合法 pos.size()>1不合法
        //2.1空 返回空
        VehicleVO vo=null;
        if (pos==null||pos.size()==0){
            log.debug("查询到的用户正在绑定车辆为空");
        }else if(pos.size()==1){
            log.debug("查询到用户正在绑定车辆:{}",pos.get(0));
            //获取车辆详情
            Integer vehicleId = pos.get(0).getVehicleId();
            ChargingVehiclePO vehiclePO=userRepository.getVehicleById(vehicleId);
            //封装vo返回
            vo=new VehicleVO();
            BeanUtils.copyProperties(vehiclePO,vo);
        }else {
            //不只查到一个 异常结束
            log.error("查询到用户正在绑定车辆数量不唯一");
        }
        return vo;
    }

    @Override
    public void bindVehicle(VehicleBindParam param) {
        //确定车辆信息可以使用牌子 最好还是使用vehicle_id
        //1.查询该用户是否存在正在使用绑定的车辆
        List<ChargingUserVehicleBindPO> vehicles = userRepository.getBindedVehicle(
                param.getUserId(),
                VehicleUserBindEnum.BINDED.getState());
        //2.判断非空
        if (vehicles!=null&&vehicles.size()>0){
            throw new RuntimeException("该用户绑定车辆,请先解绑");
        }
        //UNDO 3.验证车辆相关信息是否正确
        checkVehicleInfo(param);
        //4.使用牌子license查询vehicle中是否存在车辆信息
        ChargingVehiclePO vehiclePO=userRepository.getVehicleByLicense(param.getLicense());
        if(vehiclePO==null){
            //说明从来没有人录入过这个数据
            vehiclePO=new ChargingVehiclePO();
            //四个入参 license brand vin model 时间
            BeanUtils.copyProperties(param,vehiclePO);
            vehiclePO.setCreateTime(new Date());
            vehiclePO.setUpdateTime(new Date());
            userRepository.saveVehicle(vehiclePO);
        }
        //5.生成绑定 有用户 有车辆 只负责绑定 user_id vehicle_id state=1
        //保证如果用户对车辆解绑 state=0 重新绑定 是相同的user_id vehicle_id state=1 不出现重复数据
        //先删除 绑定数据 不一定能删除数据 在绑定
        userRepository.deleteUserBindVehicle(param.getUserId(),vehiclePO.getId());
        ChargingUserVehicleBindPO bindPo=new ChargingUserVehicleBindPO();
        bindPo.setUserId(param.getUserId());
        bindPo.setVehicleId(vehiclePO.getId());
        bindPo.setState(VehicleUserBindEnum.BINDED.getState());
        userRepository.bindUserVehicle(bindPo);

    }

    private void checkVehicleInfo(VehicleBindParam param) {
        //调用相关单位接口 如果数据信息有错误 异常结束 拒绝向后流转业务
    }

    private String getOpenId(String code) {
        //1.按照腾讯授权服务器接口的文档 拼接参数 发送请求
        RestTemplate restTemplate=new RestTemplate();
        String url="https://api.weixin.qq.com/sns/jscode2session?" +
                "appid={1}&secret={2}&js_code={3}&grant_type=authorization_code";
        //2.解析响应体 直接解析JSON字符串,封装一个对象接收WxOpenIdDTO
        //对于appid secret后端保存记录的数据 而且几乎不会变动,所以放到常量或者枚举中
        String wxLoginJson = restTemplate.getForObject(url, String.class, AppInfoConst.APP_ID, AppInfoConst.APP_SECRET, code);
        //将json利用fastjson工具解析成JSONObject(key=value) get("openid")
        JSONObject jsonObject = JSON.parseObject(wxLoginJson);
        //openid=123,  session_key=456,errorcode=123...
        String openId = jsonObject.getString("openid");
        return openId;
    }
}
